###############################################################################
#                                                                             #
#    L3q - Light, light, lightweight queue                                    #
#    Copyright (C) 2023  Marcus Pedersén marcus.pedersen@slu.se               #
#                                                                             #
#    This program is free software: you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation, either version 3 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
#    This program is distributed in the hope that it will be useful,          #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.    #
#                                                                             #
###############################################################################



import sys
import os
import grp
import configparser

'''
This module contains common classes
and functions for all L3q client programs.
'''

CONF_FILE = '/etc/l3q/l3q.conf'


class L3QConfig:
    '''
    Contains the needed configuration
    for the L3Queue clients.

    On failure program will terminate.

    Variables:
    validate_mode
    validate_uid

    are required permission and owner
    for validate file.
    '''

    validate_mode = 0o100440
    validate_uid = 0

    def __init__(self, parse_validate:bool=True) -> None:
        '''
        Init will read config file:
        /etc/l3q/l3q.conf
        If values are missing default
        values will be used.
        Validate file will also be checked
        and read.
        If any error occures error will be
        printed and program will terminate
        with non zero exit code.
        '''

        self.set_defaults()
        self.get_config()

        # After config is parsed and before
        # validation file is read,
        # check if user is part of group self.group
        # or is root
        member_of_group = False
        for gid in os.getgroups():
            gname = grp.getgrgid(gid)
            if(gname.gr_name == self.group or gid == 0):
                member_of_group = True

        if(not member_of_group):
            print('Permission denied.', file=sys.stderr)
            exit(1)

        
        if(parse_validate):
            self.get_validate()


    def set_defaults(self) -> None:
        '''
        Sets the default config values
        '''
        self.l3qd_host:str = ''
        self.l3qd_port:int = 39911
        self.group:str = 'l3q'
        self.validate_file:str = '/etc/l3q/network.l3q'
        self.log_file:str = '/var/log/l3q/l3q-client.log'
        self.validate_key:str = ''

        
    def get_config(self) -> None:
        '''
        Reads config file and populates
        object with values read from file.
        '''

        conf = configparser.ConfigParser()
        conf.read(CONF_FILE)

        if(not conf.has_section('l3q_daemon')):
            print('Error reading config file:', file=sys.stderr)
            print(CONF_FILE, file=sys.stderr)
            print('Check configuration of client tools', file=sys.stderr)
            sys.exit(1)

        if(not conf.has_option('l3q_daemon', 'l3qd_host')):
            print('Required parameter \'l3qd_host\' is missing in config file:', file=sys.stderr)
            print(CONF_FILE, file=sys.stderr)
            print('Check configuration of client tools', file=sys.stderr)
            sys.exit(2)

        self.l3qd_host = conf['l3q_daemon']['l3qd_host']

        if(conf.has_option('l3q_daemon', 'l3qd_port')):

            if(not conf['l3q_daemon']['l3qd_port'].isdigit()):
                print('Parse error in config file:', file=sys.stderr)
                print(CONF_FILE, file=sys.stderr)
                print('Parameter l3qd_port must be an integer', file=sys.stderr)
                sys.exit(3)
            else:
                self.l3qd_port = int(conf['l3q_daemon']['l3qd_port'])

        if(conf.has_option('l3q_client', 'validate_file')):
            self.validate_file = conf['l3q_client']['validate_file']

        if(conf.has_option('l3q_client', 'log_file')):
            self.log_file = conf['l3q_client']['log_file']
            
        if(conf.has_option('l3q_client', 'group')):
            self.group = conf['l3q_client']['group']


    def get_validate(self) -> None:
        '''
        Checks permissions and owner of
        the validation file, terminates
        program on failure.
        Puts validation sting in this object.
        First line of validation file is read
        so first line must contain validation key.
        File must only contain validate key.
        '''

        if(not os.path.exists(self.validate_file)):
            print('Validate file: {}'.format(self.validate_file), file=sys.stderr)
            print('Does not exist', file=sys.stderr)
            print('Configure verification with L3q daemon', file=sys.stderr)
            sys.exit(4)
            
        stat = os.stat(self.validate_file)

        if(int(stat.st_size) == 0):
            print('Validate file: {}'.format(self.validate_file), file=sys.stderr)
            print('Have zero size', file=sys.stderr)
            print('Configure verification with L3q daemon', file=sys.stderr)
            sys.exit(5)

        if(int(stat.st_mode) != L3QConfig.validate_mode):
            print('Validate file: {}'.format(self.validate_file), file=sys.stderr)
            print('has wrong file permissions', file=sys.stderr)
            print('Required mode is: {}'.format(oct(L3QConfig.validate_mode - 0o100000)[2:]), file=sys.stderr)
            sys.exit(6)

        if(int(stat.st_uid) != L3QConfig.validate_uid):
            print('Validate file: {}'.format(self.validate_file), file=sys.stderr)
            print('has wrong file owner', file=sys.stderr)
            print('Required owner is: root', file=sys.stderr)
            sys.exit(7)

        if(grp.getgrgid(stat.st_gid)[0] != self.group):
            print('Validate file: {}'.format(self.validate_file), file=sys.stderr)
            print('has wrong file group', file=sys.stderr)
            print('Required group is: {}'.format(self.group), file=sys.stderr)
            sys.exit(8)

        try:
            with open(self.validate_file) as f:
                self.validate_key = f.readline().strip()
        except Exception as e:
            print('Validate file: {}'.format(self.validate_file), file=sys.stderr)
            print('Error reading file', file=sys.stderr)
            print('{}'.format(e), file=sys.stderr)
            sys.exit(9)




